/**
 * API
 * @author wds
 * @date 2022.12.26
 */

import _ from 'lodash';
import qs from 'qs';
import Vue from 'vue';
import i18n from '../lang/index.js';
import Axios from 'axios';
import store from '../store/';
import router from '../router/index.js';
import vueIns from '../main.js';
import Storage from '../utils/Storage.js';
import * as Utils from './utils.js';
import * as Expert from '../utils/Expert.js';
import originJsonp from 'jsonp';

let resources = {};
/*
 * 封装jsonp
 * @param string url 请求的URL地址
 * @param Object params 请求的参数
 * @param Object opts 请求的设置项
 *  param: 设置回调函数名称
 *  timeout: 设置超时时间
 *  prefix：设置前缀名
 *
 * @return Promise 返回Promise对象
 * */
let jsonp = (url, params, opts = {}) => {
    return new Promise((resolve, reject) => {
        let param = qs.stringify(params);
        let uri = url + '?' + param;

        originJsonp(uri, opts, (err, data) => {
            // console.log('jsonp返回结果:', err, data);
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
};




export const setResources = res =>{
    resources = res;
};

export const getLocalResources = sourcesType => {
    // console.log('[API] getLocalResources: ', sourcesType);
    let tryNum = 0;
    return new Promise(resolve => {
        let fn = () => {
            let res;
            if (!sourcesType) {
                res = resources;
                // console.log('[API] getLocalResources: ', sourcesType, Object.keys(res).length, tryNum);
                if (Object.keys(res).length) {
                    resolve(res);
                } else if (tryNum >= 20) {
                    resolve(res);
                } else {
                    tryNum++;
                    setTimeout(fn, 200);
                }
                return;
            } else if (sourcesType === 'subscriptions') res = vueIns.$root[sourcesType];
            else res = resources && resources[sourcesType];
            // console.log('[API] getLocalResources: ', sourcesType, tryNum, ', length: ', res ? res.length : 0);
            if (typeof res !== 'undefined' && res !== null) {
                let rtn = { list: [], totalCount: 0 };
                rtn.list = _.cloneDeep(res);
                rtn.totalCount = res.length;
                resolve(rtn);
                // resolve(Object.freeze(rtn));
            } else if (tryNum >= 20) {
                resolve({ list: [], totalCount: 0 });
            } else {
                tryNum++;
                setTimeout(fn, 200);
            }
        };
        setTimeout(fn, 200);
    });
};
export const filterLocalResources = (sourceType, sourceIds) => {
    let tryNum = 0;
    return new Promise((resolve, reject) => {
        if (!sourceIds || !sourceIds.length) return reject();
        if (parseInt(sourceIds[0]) === -1) return resolve({ list: [], totalCount: 0 });
        let fn = () => {
            let res = resources && resources[sourceType];
            console.log('[API] filterLocalResources: ', sourceType, sourceIds, tryNum, res);
            if (typeof res === 'undefined' && tryNum < 30) {
                tryNum++;
                return setTimeout(fn, 200);
            }
            let list = res.filter(v => sourceIds.indexOf(v.id) !== -1);
            // console.log('[API] filterLocalResources: ', sourceType, tryNum, list);
            if (list && list.length) {
                let rtn = { list: [], totalCount: 0 };
                rtn.list = _.cloneDeep(list);
                rtn.totalCount = list.length;
                resolve(rtn);
            } else if (tryNum >= 20) {
                resolve({ list: [], totalCount: 0 });
            } else {
                tryNum++;
                setTimeout(fn, 200);
            }
        };
        setTimeout(fn, 200);
    });
};

let request = {
    //eslint-disable-line
    send: function (url, params = {}, opts = {}) {
        console.log('[API] send: ', url, params, opts);
        // loading
        if (opts && opts.loading) store.commit('OPEN_LOADING');
        // submit button
        if (opts && opts.button) opts.button.disabled = true;

        // 2. 组装请求的param
        let sess = Storage.getSessionStorage('CREDENTIALS');
        let opt = opts || {},
            param = {
                url: url,
                method: opts.method || 'get',
                headers: {
                    // // 用户认证token
                    // 'access-token': Storage.getSessionStorage().get('token')
                    Authorization: sess.get('authorHeader')
                },
                validateStatus: null, // all server return status is resolve
                params: {}
            };
        if (sess.get('token')) param.headers.Token = sess.get('token');
        console.log('[API] send token => ', sess.get('token'), param);

        // // 服务器端的指定标识，用于区分多公众号及各个下单平台的标识，默认标识：20（代表H5）。 Dio Zhu. on 2017.12.22
        // param.params['access_source'] = Storage.getSessionStorage().get('access_source') || 20;

        // 开启表单模式，转换对象、数组为string
        if (opt.emulateJSON) {
            param.headers['Content-Type'] = 'application/x-www-form-urlencoded';
            for (let k in params)
                if (params.hasOwnProperty(k) && Object.prototype.toString.call(params[k]) === '[object Object]')
                    params[k] = JSON.stringify(params[k]);
            // 转换数据格式
            param.transformRequest = [
                function (data) {
                    return qs.stringify(data);
                }
            ];
        }

        if (!param || !param.method || param.method.toLowerCase() === 'get') {
            param.params = Object.assign(param.params, params);
            param.paramsSerializer = function (data) {
                return qs.stringify(data);
            };
        } else {
            param.data = params;
        }

        // 4. 发起请求
        // console.log(`${url}请求的参数：`, param);
        return Axios
            .request(param)
            .then(res => {
                console.log(`[API] success: `, res);
                if (opts && opts.loading) store.dispatch('CLOSE_LOADING');
                if (opts && opts.button) opts.button.disabled = false;

                if (res.status === 200) {
                    if (
                        res &&
                        res.data &&
                        (res.data === 404 || (Array.isArray(res.data) && res.data.indexOf(404) >= 0))
                    ) {
                        vueIns.$message({ type: 'error', message: i18n.t('lang.resourceNotFound') });
                    } else if (
                        res &&
                        res.data &&
                        (res.data === 403 || (Array.isArray(res.data) && res.data.indexOf(403) >= 0))
                    ) {
                        vueIns.$message({ type: 'error', message: i18n.t('lang.accessDenied') });
                    } else if (res && res.data && res.data.status === 1001) {
                        // UNAUTHORIZED
                        if (!opts || !opts.silent)
                            vueIns.$message({ type: 'error', message: i18n.t('lang.wrongCredentials') });
                        router.push({ name: 'author' });
                        return Promise.reject(res);
                    } else if (
                        res &&
                        res.data &&
                        (res.data === 405 || (Array.isArray(res.data) && res.data.indexOf(405) >= 0))
                    ) {
                        vueIns.$message({ type: 'error', message: i18n.t('lang.vehicleTask') });
                    } else {
                        return Promise.resolve(res.data);
                    }
                } else if (res.status === 201) {
                    // CREATED
                    let message = i18n.t('lang.resource') + ' ' + i18n.t('lang.created');
                    if (!opts || !opts.silent) vueIns.$message({ type: 'success', message });
                    let createdResourceId = res.headers && res.headers['location'];
                    return Promise.resolve(createdResourceId || request.status);
                } else if (res.status === 204) {
                    // NO_CONTENT
                    let message = i18n.t('lang.resource') + ' ';
                    message += opts.method === 'DELETE' ? i18n.t('lang.deleted') : i18n.t('lang.updated');
                    if (!opts || !opts.silent) vueIns.$message({ type: 'success', message });
                    return Promise.resolve(request.status);
                } else if (res.status === 207) {
                    // MULTI_STATUS
                    let results = res.data,
                        message = i18n.t('lang.unknownError'),
                        counts = {};
                    for (let i = 0; i < results.length; i++) {
                        counts[results[i]] = counts[results[i]] ? counts[results[i]] + 1 : 1;
                    }
                    let successCount = counts[201] || counts[204];
                    if (successCount) {
                        message = successCount + '/' + results.length + ' ' + i18n.t('lang.resources') + ' ';
                        if (params[0].method === 'DELETE') message += i18n.t('lang.deleted');
                        else message += counts[204] ? i18n.t('lang.updated') : i18n.t('lang.created');
                    } else if (counts[400]) message = i18n.t('lang.badRequest');
                    else if (counts[401]) message = i18n.t('lang.wrongCredentials');
                    else if (counts[403]) message = i18n.t('lang.accessDenied');
                    else if (counts[404]) message = i18n.t('lang.resourceNotFound');
                    else if (counts[422]) message = i18n.t('lang.resourceUnprocessable');
                    // let color = successCount === results.length ? 'green' : successCount > 0 ? 'yellow' : 'red';
                    // if (button) vueIns.$toast(message, color, button);
                    if (!opts || !opts.silent) vueIns.$message({ type: 'warning', message });
                    return Promise.resolve(request);
                } else if (res.status === 400) {
                    // BAD_REQUEST
                    if (!opts || !opts.silent) vueIns.$message({ type: 'error', message: i18n.t('lang.badRequest') });
                } else if (res.status === 401) {
                    // UNAUTHORIZED
                    if (!opts || !opts.silent)
                        vueIns.$message({ type: 'error', message: i18n.t('lang.wrongCredentials') });
                    router.push({ name: 'author' });
                    return Promise.reject(res);
                } else if (res.status === 403) {
                    // FORBIDDEN
                    if (!opts || !opts.silent) vueIns.$message({ type: 'error', message: i18n.t('lang.accessDenied') });
                } else if (res.status === 404) {
                    // NOT_FOUND
                    if (!opts || !opts.silent)
                        vueIns.$message({ type: 'error', message: i18n.t('lang.resourceNotFound') });
                } else if (res.status === 422) {
                    // UNPROCESSABLE_ENTITY
                    if (!opts || !opts.silent)
                        vueIns.$message({ type: 'error', message: i18n.t('lang.resourceUnprocessable') });
                }
                return Promise.reject(res);
            })
            .catch(e => {
                console.error(`[API] error: `, e);
                if (opts && opts.loading) store.dispatch('CLOSE_LOADING');
                if (opts && opts.button) opts.button.disabled = false;
                return Promise.reject(e);
            });
    },
    sendBatch: function (params = {}, opts = {}) {
        let opt = Object.assign(opts, { method: 'POST' });
        return this.send(`${process.env.VUE_APP_RESTFUL_URL}/batch`, params, opt);
    }
};
export const getBaiduTest = (params = { ak: 'zEI6dr0ESfSc7BRGd2FVs3G9gdMxRWYQ' }, opts) =>
    jsonp('//api.map.baidu.com/geoconv/v1/', { ak: 'zEI6dr0ESfSc7BRGd2FVs3G9gdMxRWYQ', ...params }, opts);
export async function oauth(username, password, button) {
    let res = await request.send(process.env.VUE_APP_OAUTH_URL, { username, password }, { button });
    if (!res || res.status !== 0 || !res.data) return Promise.reject(res);
    Storage.getSessionStorage('CREDENTIALS').set('token', res.data);
    return Promise.resolve(res);
}

export function storeCredentials(username, password) {
    let sess = Utils.getSessionStorage('CREDENTIALS');
    sess.set('username', username);
    sess.set('authorHeader', 'Basic ' + Expert.auth(username, password));
}
// export const subscriptions = (method, button, object, silentSuccess) =>
//     REST.sendRequest(method, process.env.VUE_APP_RESTFUL_URL + '/subscriptions', button, object, silentSuccess);
export const loadScene = () => request.send(`${process.env.VUE_APP_RESTFUL_URL}/loadScene`, {}, { method: 'POST' });

export async function subscriptions(object, opts) {
    let subscriptionsUrl = process.env.VUE_APP_RESTFUL_URL + '/subscriptions';
    let res = await request
        .send(subscriptionsUrl, object, opts)
        .catch(e => Promise.reject(e));
    Storage.getSessionStorage('CREDENTIALS').set('subscriptions', object);
    Storage.getSessionStorage('CREDENTIALS').set('wstoken', res);
    return Promise.resolve(res);
}
export const toggleLock = (type, ids, val, button) => {
    let params = [];
    (ids || []).forEach(id => {
        params.push({ method: 'PUT', path: type + '/' + id + '/lock', body: val });
    });
    return request.sendBatch(params, { button });
};
export const toggleUpdates = (type, ids, property, val, button) => {
    let params = [];
    (ids || []).forEach(id => {
        params.push({ method: 'PUT', path: `${type}/${id}/${property}`, body: val });
    });
    return request.sendBatch(params, { button });
};
export const batchDelete = (module, ids, button) => {
    if (!ids || !ids.length) return;
    let params = [];
    (ids || []).forEach(id => params.push({ method: 'DELETE', path: `${module}/` + id }));
    return request.sendBatch(params, { button });
};
export const moduleUpdate = (module, obj, button) => {
    let url = `${process.env.VUE_APP_RESTFUL_URL}/${module}`,
        method = 'POST';
    if (obj.id >= 0 || (obj.id && isNaN(parseInt(obj.id)))) {
        url += `/${obj.id}`;
        method = 'PUT';
    }
    return request.send(url, obj, { method, button });
};

export const modulesUpdate = (module, objs, button) => {
    let params = [];
    (objs || []).forEach(o => {
        let p = { body: JSON.stringify(Object.assign({}, o)) };
        if (o.id >= 0) {
            p.method = 'PUT';
            p.path = `${module}/${o.id}`;
        } else {
            p.method = 'POST';
            p.path = module;
        }
        params.push(p);
    });
    return request.sendBatch(params, { button });
};
export const moduleGet = (module, id) => request.send(process.env.VUE_APP_RESTFUL_URL + `/${module}/${id}`);
export const modulesGet = module => request.send(process.env.VUE_APP_RESTFUL_URL + `/${module}`);
export const moduleUpload = (module, obj, opt) => {
    let opts = Object.assign({ method: 'PUT' }, opt);
    return request.send(process.env.VUE_APP_RESTFUL_URL + `/${module}/${obj.id}`, obj, opts);
};
export const modulesUpload = (module, list, opt) => {
    let opts = Object.assign({ method: 'PUT' }, opt);
    return request.send(process.env.VUE_APP_RESTFUL_URL + `/${module}`, list, opts);
};
export const mergeModulesUpload = (module, params, opt) => {
    let opts = Object.assign({ method: 'POST' }, opt);
    return request.send(process.env.VUE_APP_RESTFUL_URL + `/batch`, params, opts);
};

/**
 * logs
 */
export const getLogfile = filename => {
    let url = process.env.VUE_APP_RESTFUL_URL + '/logfiles/' + filename;
    return new Promise(resolve => {
        const xhr = new XMLHttpRequest();
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4 && xhr.status === 200) {
                let rtn = xhr.responseText.replace(/\n/g, '<br><br>');
                resolve(rtn);
            }
        };
        xhr.open('GET', url);
        xhr.send();
    });
};

/**
 * scripts
 */
export const getScripts = () => modulesGet('scripts');
export const updateScript = (obj, button) => moduleUpdate('scripts', obj, button);

/**
 * variables
 */
export const getVariable = id => moduleGet('variables', id);
export const getVariables = () => modulesGet('variables');
export const updateVariables = (obj, button) => moduleUpdate('variables', obj, button);

/**
 * users
 */
export const getCurrentUserId = () => Storage.getSessionStorage('CREDENTIALS').get('username');
export const getUserById = async id => {
    if (!id) return;
    let users = await filterLocalResources('users', id);
    if (users && users.list && users.list.length) return users.list[0];
    else return null;
};
export const getUsers = () => modulesGet('users');
export const updateUser = (obj, button) => moduleUpdate('users', obj, button);
export const updateUserPwd = (username, param, button) => {
    let url = `${process.env.VUE_APP_RESTFUL_URL}/users/${username}/pwChangeRequest`;
    return request.send(url, param, { method: 'PUT', emulateJSON: true, button });
};
// export const updateUserPwd = (username, password, button) => {
//     let url = `${process.env.VUE_APP_RESTFUL_URL}/users/${username}/pwChangeRequest`;
//     return request.send(url, password, { method: 'PUT', button });
// };

/**
 * settings
 */
export async function getSettings() {
    let res = await modulesGet('settings');
    Storage.getSessionStorage('SETTINGS').set('settings', res);
    return Promise.resolve(res);
}

export const updateSetting = (obj, button) => moduleUpdate('settings', obj, button);

/**
 * contours
 */
export const getContour = id => moduleGet('contours', id);
export const getContours = () => modulesGet('contours');
export const updateContours = (list, button) => modulesUpdate('contours', list, button);
export const uploadContours = (file, button) => modulesUpload('contours', file.objects, { button });

/**
 * ranges
 */
export const getRange = id => moduleGet('ranges', id);
export const getRanges = () => modulesGet('ranges');
export const updateRanges = (list, button) => modulesUpdate('ranges', list, button);
export const uploadRanges = (file, button) => modulesUpload('ranges', file.objects, { button });

/**
 * models
 */
export const getModel = id => moduleGet('models', id);
export const getModels = () => modulesGet('models');
export const uploadModels = (file, button) => {
    let model = JSON.parse(file.contentAsString);
    request.send(`${process.env.VUE_APP_RESTFUL_URL}/models/${model.id}`, button, model);
};
export const updateModel = (obj, button) => moduleUpdate('models', obj, button);
export const updateModels = (list, button) => modulesUpdate('models', list, button);

/**
 * vehicles
 */
export const getVehicle = id => moduleGet('vehicles', id);
export const getVehicles = () => modulesGet('vehicles');
export const updateVehicle = (obj, button) => moduleUpdate('vehicles', obj, button);
export const updateVehicleRemote = (id, state, opt) => {
    if (!id) return;
    let opts = Object.assign({ method: 'PUT', emulateJSON: true }, opt);
    // return REST.sendRequest('PUT', process.env.VUE_APP_RESTFUL_URL + '/vehicles/' + id + '/remote', button, state);
    return request.send(`${process.env.VUE_APP_RESTFUL_URL}/vehicles/${id}/remote`, { value: state }, opts);
};
/**
 * vehicles
 */
export const updateVehicleStop = (params, opt) => {
    let opts = Object.assign({ method: 'POST' }, opt);
    return request.send(process.env.VUE_APP_RESTFUL_URL + `/batch`, params, opts);
};

/**
 * paths
 */
export const getPath = id => moduleGet('paths', id);
export const getPaths = () => modulesGet('paths');
export const updatePaths = (list, button) => modulesUpdate('paths', list, button);
export const mergePaths = (ids, button) => {
    if (!ids || !ids.length) return;
    return request.send(`${process.env.VUE_APP_RESTFUL_URL}/paths/merge`, ids, { method: 'POST', button });
};
export const splitPaths = (ids, button) => {
    if (!ids || !ids.length) return;
    return request.send(`${process.env.VUE_APP_RESTFUL_URL}/paths/split`, ids, { method: 'POST', button });
};

/**
 * jobs
 */
export const getJob = id => moduleGet('jobs', id);
export const getJobs = () => modulesGet('jobs');
export const updateJob = (obj, button) => moduleUpdate('jobs', obj, button);
export const updateJobs = (list, button) => modulesUpdate('jobs', list, button);
export const updateJobsReset = (ids, button) => {
    let params = [];
    (ids || []).forEach(id => {
        params.push({ method: 'PUT', path: `jobs/${id}/initRequest`, body: 'true' });
    });
    return request.sendBatch(params, { button });
};

// export default REST;